package com.zhichat.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.zhichat.dao.*;
import com.zhichat.document.ChatMsgDoc;
import com.zhichat.entity.*;
import com.zhichat.infrastructure.Enums.*;
import com.zhichat.infrastructure.config.OSSConfig;
import com.zhichat.infrastructure.constants.Constants;
import com.zhichat.infrastructure.exception.http.CommonException;
import com.zhichat.infrastructure.redis.Keys;
import com.zhichat.infrastructure.redis.RedisRepository;
import com.zhichat.infrastructure.redis.rediskey.Key;
import com.zhichat.infrastructure.redis.rediskey.ZhiChat;
import com.zhichat.infrastructure.thread.ThreadPoolWorker;
import com.zhichat.infrastructure.util.OSSClient;
import com.zhichat.infrastructure.util.ThreadLocalCache;
import com.zhichat.infrastructure.util.Utils;
import com.zhichat.service.*;
import com.zhichat.web.api.PageUtils;
import com.zhichat.web.dto.AddOrRemoveMemberDTO;
import com.zhichat.web.dto.AdminGroupQueryDTO;
import com.zhichat.web.dto.GroupInfoAddOrUpdateDTO;
import com.zhichat.web.dto.dao.AdminGroupListQuery;
import com.zhichat.web.socket.ChannelContextCommon;
import com.zhichat.web.socket.GlobalMsgHandle;
import com.zhichat.web.vo.*;
import com.zhichat.web.vo.ws.MsgVO;
import io.netty.channel.Channel;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
@AllArgsConstructor
public class GroupInfoServiceImpl extends ServiceImpl<GroupInfoDao, GroupInfoEntity> implements GroupInfoService {

    private final RedisRepository redisRepository;
    private final ContactInfoDao contactInfoDao;
    private final OSSConfig ossConfig;
    private final ChatSessionDao chatSessionDao;
    private final ChatUserSessionDao chatUserSessionDao;
    private final GlobalMsgHandle globalMsgHandle;
    private final ChannelContextCommon channelContextCommon;
    private final MongoTemplate mongoTemplate;
    private final ContactInfoService contactInfoService;
    private final SysConfigService sysConfigService;
    private final UserInfoDao userInfoDao;
    private final ChatSessionService chatSessionService;
    private final ChatUserSessionService chatUserSessionService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean createGroup(GroupInfoAddOrUpdateDTO dto) throws IOException {
        String groupName = dto.getGroupName();
        LocalDateTime curDate = LocalDateTime.now();
        MultipartFile groupAvatarFile = dto.getGroupAvatarFile();
        MultipartFile groupAvatarCoverFile = dto.getGroupAvatarCoverFile();
        if (groupAvatarFile.isEmpty() || groupAvatarCoverFile.isEmpty()) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        // 判断 创建的群，是否达到最大值
        LambdaQueryWrapper<GroupInfoEntity> groupInfoEntityLambdaQueryWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
        groupInfoEntityLambdaQueryWrapper.eq(GroupInfoEntity::getGroupOwnerId, ThreadLocalCache.getUserId())
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode());
        List<GroupInfoEntity> groupList = baseMapper.selectList(groupInfoEntityLambdaQueryWrapper);
        Key sysKey = new ZhiChat(Keys.SYS_KEY);
        SysConfigEntity sysConfigEntity = JSONObject.parseObject(redisRepository.get(sysKey), SysConfigEntity.class);
        if (groupList.size() >= sysConfigEntity.getMaxGroupCount()) {
            throw new CommonException("群聊已达到最大值，无法创建新的群聊");
        }
        // 校验 群名称是否有重复
        if (groupList.stream().anyMatch(x -> Objects.equals(groupName, x.getGroupName()))) {
            throw new CommonException("群名称已存在");
        }
        GroupInfoEntity groupInfoEntity = new GroupInfoEntity();
        BeanUtils.copyProperties(dto, groupInfoEntity);
        Long groupId = Utils.snowflakeId();
        groupInfoEntity.setGroupId(groupId);
        groupInfoEntity.setGroupOwnerId(ThreadLocalCache.getUserId());
        // 新增 群信息 -> 数据库
        if (baseMapper.insert(groupInfoEntity) != 1) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        // 将 群组 添加到 联系人
        ContactInfoEntity contactInfoEntity = new ContactInfoEntity();
        contactInfoEntity.setUserId(ThreadLocalCache.getUserId());
        contactInfoEntity.setContactId(groupInfoEntity.getGroupId());
        contactInfoEntity.setContactType(ContactTypeEnum.GROUP.getCode());
        contactInfoEntity.setLastUpdateTime(LocalDateTimeUtil.toEpochMilli(curDate));
        contactInfoEntity.setStatus(ContactStatusEnum.FRIEND.getCode());
        if (contactInfoDao.insert(contactInfoEntity) != 1) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        // 头像保存到 OSS 中，目录 定义 zhichat/groupAvatar/{groupId}.jpg
        // 分别处理 原图 和 缩略图
        // 是否要？ - 图片先下载到本地，下次获取先从本地获取，本地没有再从远程获取（每个用户需要有配置表）  dto.getGroupAvatarFile().transferTo(new File("本地路径"));
        InputStream groupAvatarFileIn = groupAvatarFile.getInputStream();
        InputStream groupAvatarCoverFileIn = groupAvatarCoverFile.getInputStream();
        ThreadPoolWorker.singleInstance().getExecutor().execute(() -> {
            if (!saveAvatar(groupAvatarFileIn, groupAvatarCoverFileIn, groupId)) {
                log.info("群：{} - 头像保存失败", groupId);
            }
        });
        String msgContent = StrUtil.format(MsgTypeEnum.GROUP_CREATE.getInitMsg(), groupName);
        // 添加会话信息 -> mysql
        String sessionId = Utils.lsMd5(groupId, ThreadLocalCache.getUserId());
        ChatSessionEntity chatSessionEntity = new ChatSessionEntity()
                .setId(sessionId)
                .setLastReceiveTime(DateUtil.date(curDate).getTime())
                .setLastMsg(msgContent);
        if (chatSessionDao.insert(chatSessionEntity) != 1) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        ChatUserSessionEntity chatUserSessionEntity = new ChatUserSessionEntity()
                .setSessionId(sessionId)
                .setUserId(ThreadLocalCache.getUserId())
                .setReceiverId(groupId)
                .setReceiverType(ContactTypeEnum.GROUP.getCode())
                .setReceiverNickname(groupName);
        if (1 != chatUserSessionDao.insert(chatUserSessionEntity)) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        // 添加群联系人 -> redis
        Key key = new ZhiChat(Keys.CONTACT_GROUP);
        redisRepository.add_2(key, Sets.newHashSet(groupId), ThreadLocalCache.getUserId());
        // 添加用户channel -> 群channel
        channelContextCommon.putAllUserChannelToGroupChannel(groupId, Lists.newArrayList(channelContextCommon.getUserChannel(ThreadLocalCache.getUserId())));
        // 添加消息 -> mongodb
        ChatMsgDoc chatMsgDoc = new ChatMsgDoc();
        try {
            chatMsgDoc.setId(Utils.uniqueId())
                    .setSessionId(sessionId)
                    .setSenderId(ThreadLocalCache.getUserId())
                    .setSenderNickname(ThreadLocalCache.getUserToken().getNickname())
                    .setSendTime(DateUtil.date(curDate).getTime())
                    .setReceiverId(groupId)
                    .setReceiverType(ContactTypeEnum.GROUP.getCode())
                    .setMsgType(MsgTypeEnum.GROUP_CREATE.getCode())
                    .setMsg(msgContent)
                    .setStatus(ChatMsgStatusEnum.SUCCESS.getCode());
            mongoTemplate.insert(chatMsgDoc);
        } catch (Exception e) {
            throw CommonException.MONGO_MODIFY_COMMON_EXCEPTION;
        }
        // 发送ws消息
        MsgVO<String> msgVO = new MsgVO<>();
        BeanUtils.copyProperties(chatMsgDoc, msgVO);
        msgVO.setMsgId(chatMsgDoc.getId())
                .setReceiverNickname(groupName)
                .setMemberCount(1);
        globalMsgHandle.sendHandler(msgVO);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateGroup(GroupInfoAddOrUpdateDTO dto) throws IOException {
        long curTime = System.currentTimeMillis();
        Long groupId = dto.getGroupId();
        LambdaQueryWrapper<GroupInfoEntity> queryWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
        queryWrapper.select(GroupInfoEntity::getGroupOwnerId, GroupInfoEntity::getGroupName)
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode())
                .eq(GroupInfoEntity::getGroupId, groupId);
        GroupInfoEntity entity = baseMapper.selectOne(queryWrapper);
        // 判断是否是 群主本人修改群信息
        if (Objects.isNull(entity) || !Objects.equals(entity.getGroupOwnerId(), ThreadLocalCache.getUserId())) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        String groupName = dto.getGroupName();
        boolean updateGroupNameFlag = StringUtils.hasText(groupName) && !Objects.equals(entity.getGroupName(), groupName);
        // 如果修改了群名称，校验 群名称是否有重复
        if (updateGroupNameFlag) {
            LambdaQueryWrapper<GroupInfoEntity> query = Wrappers.lambdaQuery(GroupInfoEntity.class);
            query.eq(GroupInfoEntity::getGroupOwnerId, ThreadLocalCache.getUserId())
                    .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode())
                    .eq(GroupInfoEntity::getGroupName, dto.getGroupName());
            Integer checkGourpNameCount = Math.toIntExact(baseMapper.selectCount(query));
            if (checkGourpNameCount > 0) {
                throw new CommonException("群名称已存在");
            }
        }
        // 修改 群信息 -> 数据库
        GroupInfoEntity groupInfoEntity = new GroupInfoEntity();
        BeanUtils.copyProperties(dto, groupInfoEntity);
        int updateRow = baseMapper.updateById(groupInfoEntity);
        if (updateRow != 1) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        MultipartFile groupAvatarFile = dto.getGroupAvatarFile();
        MultipartFile groupAvatarCoverFile = dto.getGroupAvatarCoverFile();
        // 头像 上传 保存
        InputStream groupAvatarFileIn = groupAvatarFile.getInputStream();
        InputStream groupAvatarCoverFileIn = groupAvatarCoverFile.getInputStream();
        ThreadPoolWorker.singleInstance().getExecutor().execute(() -> {
            if (!saveAvatar(groupAvatarFileIn, groupAvatarCoverFileIn, groupId)) {
                log.info("群：{} - 头像保存失败", groupId);
            }
        });
        // 【跟新 群名称 冗余在其他表的信息】 和 【发送ws消息】
        if (updateGroupNameFlag) {
            LambdaUpdateWrapper<ChatUserSessionEntity> chatUserSessionUpdate = Wrappers.lambdaUpdate(ChatUserSessionEntity.class);
            chatUserSessionUpdate.eq(ChatUserSessionEntity::getReceiverId, groupId)
                    .set(ChatUserSessionEntity::getReceiverNickname, groupName);
            if (chatUserSessionDao.update(null, chatUserSessionUpdate)
                    != chatUserSessionDao.selectCount(Wrappers.lambdaQuery(ChatUserSessionEntity.class).eq(ChatUserSessionEntity::getReceiverId, groupId))) {
                throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
            }
            // 发送ws信息（TODO 修改 头像、入群审核）
            MsgVO<String> msgVO = new MsgVO<>();
            msgVO.setMsgType(MsgTypeEnum.UPDATE_GROUP_NICKNAME.getCode())
                    .setSendTime(curTime)
                    .setSenderId(ThreadLocalCache.getUserId())
                    .setSenderNickname(ThreadLocalCache.getUserToken().getNickname())
                    .setReceiverId(groupId)
                    .setReceiverNickname(groupName)
                    .setReceiverType(ContactTypeEnum.GROUP.getCode())
                    .setMsg(StrUtil.format(MsgTypeEnum.UPDATE_GROUP_NICKNAME.getInitMsg(), ThreadLocalCache.getUserToken().getNickname(), groupName))
                    .setData(groupName);
            globalMsgHandle.sendHandler(msgVO);
        }
        return true;
    }

    private Boolean saveAvatar(InputStream groupAvatarFileIn, InputStream groupAvatarCoverFileIn, Long groupId) {
        Boolean groupAvatarFileFlag = OSSClient.uploadFile(groupAvatarFileIn, Constants.GROUP_AVATAR_OSS_FOLDER
                , groupId.toString().concat(Constants.IMG_SUF_PNG), ossConfig, null);
        if (!groupAvatarFileFlag) {
            throw CommonException.FILE_OPERATE_COMMON_EXCEPTION;
        }
        Boolean groupAvatarCoverFileFlag = OSSClient.uploadFile(groupAvatarCoverFileIn, Constants.GROUP_AVATAR_OSS_FOLDER
                , groupId.toString().concat(Constants.IMG_SUF_PNG.concat(Constants.COVER_SUF_JPG)), ossConfig, null);
        if (!groupAvatarCoverFileFlag) {
            throw CommonException.FILE_OPERATE_COMMON_EXCEPTION;
        }
        return true;
    }

    @Override
    public List<GroupListVO> listGroup() {
        // 查询所有正常状态的群，进行返回
        LambdaQueryWrapper<GroupInfoEntity> queryWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
        queryWrapper.select(GroupInfoEntity::getGroupId, GroupInfoEntity::getGroupName, GroupInfoEntity::getGroupOwnerId)
                .eq(GroupInfoEntity::getGroupOwnerId, ThreadLocalCache.getUserId())
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode())
                .orderByDesc(GroupInfoEntity::getGroupId);
        List<GroupInfoEntity> groupInfoEntities = baseMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(groupInfoEntities)) {
            return Lists.newArrayList();
        }
        List<GroupListVO> resList = groupInfoEntities.stream().map(x -> {
            GroupListVO vo = new GroupListVO();
            BeanUtils.copyProperties(x, vo);
            return vo;
        }).collect(Collectors.toList());
        return resList;
    }

    @Override
    public GroupDetailVO detail(Long groupId) {
        GroupInfoEntity groupInfoEntity = getGroupDetail(groupId);
        // 查询群成员数量
        LambdaQueryWrapper<ContactInfoEntity> contactInfoEntityLambdaQueryWrapper = Wrappers.lambdaQuery(ContactInfoEntity.class);
        contactInfoEntityLambdaQueryWrapper.eq(ContactInfoEntity::getContactId, groupId)
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode());
        Integer count = Math.toIntExact(contactInfoDao.selectCount(contactInfoEntityLambdaQueryWrapper));
        // 返回群信息
        GroupDetailVO vo = new GroupDetailVO();
        BeanUtils.copyProperties(groupInfoEntity, vo);
        vo.setMemberCount(count);
        return vo;
    }

    @Override
    public GroupDetailAndMembersVO detailAndMembers(Long groupId) {
        GroupInfoEntity groupInfoEntity = getGroupDetail(groupId);
        // 查询群成员
        List<GroupMemberDetailVO> groupMembersDetailList = baseMapper.getGroupMembersDetailList(groupId);
        // 返回数据
        GroupDetailAndMembersVO vo = new GroupDetailAndMembersVO();
        BeanUtils.copyProperties(groupInfoEntity, vo);
        vo.setMemberCount(groupMembersDetailList.size());
        vo.setMembers(groupMembersDetailList);
        return vo;
    }

    @Override
    public PageUtils<AdminGroupInfoVO> groupListOfAdmin(AdminGroupQueryDTO dto) {
        AdminGroupListQuery query = new AdminGroupListQuery();
        BeanUtils.copyProperties(dto, query);
        query.setStart(dto.getPageSize() * (dto.getPageNum() - 1));
        query.setSize(dto.getPageSize());
        List<AdminGroupInfoVO> dataList = baseMapper.getGroupInfoListOfAdmin(query);
        return new PageUtils<>(dataList, Math.toIntExact(baseMapper.selectCount(null))
                , dto.getPageSize(), dto.getPageNum());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean disbandGroupByAdmin(Long groupId, Long groupOwnerId) {
        // 校验群是否存在
        LambdaQueryWrapper<GroupInfoEntity> queryWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
        queryWrapper.eq(GroupInfoEntity::getGroupId, groupId)
                .eq(GroupInfoEntity::getGroupOwnerId, groupOwnerId)
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode());
        GroupInfoEntity groupInfoEntity = baseMapper.selectOne(queryWrapper);
        if (Objects.isNull(groupInfoEntity)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        return disbandGroup(groupId, groupOwnerId);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean disbandGroup(Long groupId, Long groupOwnerId) {
        // 修改群关联的 所有群员 的好友状态
        LambdaQueryWrapper<ContactInfoEntity> contactQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
        contactQuery.select(ContactInfoEntity::getUserId)
                .eq(ContactInfoEntity::getContactId, groupId)
                .eq(ContactInfoEntity::getContactType, ContactTypeEnum.GROUP.getCode())
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode());
        List<ContactInfoEntity> contactInfoEntities = contactInfoDao.selectList(contactQuery);
        LambdaUpdateWrapper<ContactInfoEntity> contactUpdateWrapper = Wrappers.lambdaUpdate(ContactInfoEntity.class);
        contactUpdateWrapper.eq(ContactInfoEntity::getContactId, groupId)
                .eq(ContactInfoEntity::getContactType, ContactTypeEnum.GROUP.getCode())
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode())
                .set(ContactInfoEntity::getStatus, ContactStatusEnum.DELETED_FRIEND.getCode());
        if (contactInfoDao.update(null, contactUpdateWrapper) == 0) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        // 修改群状态 -> mysql
        LambdaUpdateWrapper<GroupInfoEntity> updateWrapper = Wrappers.lambdaUpdate(GroupInfoEntity.class);
        updateWrapper.eq(GroupInfoEntity::getGroupId, groupId)
                .eq(GroupInfoEntity::getGroupOwnerId, groupOwnerId)
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode())
                .set(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_DELETE.getCode());
        if (0 == baseMapper.update(null, updateWrapper)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        long curTime = DateUtil.current();
        String sessionId = Utils.lsMd5(groupId);
        String msg = MsgTypeEnum.GROUP_DISMISS.getInitMsg();
        // 修改会话信息 -> mysql
        ChatSessionEntity updateChatSession = new ChatSessionEntity()
                .setLastMsg(msg)
                .setId(sessionId)
                .setLastReceiveTime(curTime);
        if (1 != chatSessionDao.updateById(updateChatSession)) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        // 添加消息 -> mongo
        ChatMsgDoc chatMsgDoc = new ChatMsgDoc()
                .setId(Utils.uniqueId())
                .setSessionId(sessionId)
                .setSendTime(curTime)
                .setReceiverType(ContactTypeEnum.GROUP.getCode())
                .setStatus(ChatMsgStatusEnum.SUCCESS.getCode())
                .setMsgType(MsgTypeEnum.GROUP_DISMISS.getCode())
                .setMsg(msg)
                .setReceiverId(groupId);
        mongoTemplate.insert(chatMsgDoc);
        // 清除缓存
        contactInfoEntities.forEach(contactInfoEntity -> {
            Key key = new ZhiChat(Keys.CONTACT_GROUP);
            redisRepository.sRemove(key, Sets.newHashSet(groupId), contactInfoEntity.getUserId());
        });
        // 发送ws消息
        MsgVO<String> msgVO = new MsgVO<>();
        BeanUtils.copyProperties(chatMsgDoc, msgVO);
        msgVO.setMsgId(chatMsgDoc.getId());
        globalMsgHandle.sendHandler(msgVO);
        return true;
    }

    private GroupInfoEntity getGroupDetail(Long groupId) {
        // 判断 用户是否在这个群中
        LambdaQueryWrapper<ContactInfoEntity> contactInfoEntityLambdaQueryWrapper = Wrappers.lambdaQuery(ContactInfoEntity.class);
        contactInfoEntityLambdaQueryWrapper.eq(ContactInfoEntity::getUserId, ThreadLocalCache.getUserId())
                .eq(ContactInfoEntity::getContactId, groupId)
                .eq(ContactInfoEntity::getContactType, ContactTypeEnum.GROUP.getCode())
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode());
        ContactInfoEntity contactInfoEntity = contactInfoDao.selectOne(contactInfoEntityLambdaQueryWrapper);
        if (Objects.isNull(contactInfoEntity)) {
            throw new CommonException("群不存在");
        }
        // 查询群信息
        LambdaQueryWrapper<GroupInfoEntity> queryWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
        queryWrapper.eq(GroupInfoEntity::getGroupId, groupId)
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode());
        GroupInfoEntity groupInfoEntity = baseMapper.selectOne(queryWrapper);
        if (Objects.isNull(groupInfoEntity)) {
            throw new CommonException("群不存在");
        }
        return groupInfoEntity;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addOrRemoveMember(AddOrRemoveMemberDTO dto) {
        // 校验参数
        List<Long> ids = CollUtil.newArrayList(dto.getIds().split(","))
                .stream().map(Long::valueOf).collect(Collectors.toList());
        Long groupId = dto.getGroupId();
        Integer type = dto.getType();
        if (!ArrayUtil.contains(new int[]{1, 2}, type)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        LambdaQueryWrapper<GroupInfoEntity> queryWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
        queryWrapper.select(GroupInfoEntity::getGroupName, GroupInfoEntity::getGroupOwnerId)
                .eq(GroupInfoEntity::getGroupId, groupId)
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode());
        GroupInfoEntity groupInfo = this.getOne(queryWrapper);
        if (ObjectUtil.isNull(groupInfo)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        // 是添加还是移除
        if (1 == type) {
            // 添加成员
            userBatchAddGroup(groupId, groupInfo.getGroupName(), Sets.newHashSet(ids));
        } else {
            // 该操作只能是管理员
            if (ObjectUtil.notEqual(groupInfo.getGroupOwnerId(), ThreadLocalCache.getUserId())) {
                throw CommonException.PARAMETER_COMMON_EXCEPTION;
            }
            // 移除成员
            userBatchLeaveGroup(groupId, groupInfo.getGroupName(), Sets.newHashSet(ids), MsgTypeEnum.KICK_GROUP);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean quitGroup(Long groupId) {
        Long userId = ThreadLocalCache.getUserId();
        // 不是群主
        LambdaQueryWrapper<GroupInfoEntity> queryWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
        queryWrapper.select(GroupInfoEntity::getGroupName, GroupInfoEntity::getGroupOwnerId)
                .eq(GroupInfoEntity::getGroupId, groupId)
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode());
        GroupInfoEntity groupInfo = this.getOne(queryWrapper);
        if (ObjectUtil.isNull(groupInfo) || ObjectUtil.equal(userId, groupInfo.getGroupOwnerId())) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        // 退出群聊
        userBatchLeaveGroup(groupId, groupInfo.getGroupName(), Sets.newHashSet(userId), MsgTypeEnum.EXIT_GROUP);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public void userBatchAddGroup(Long groupId, String groupNickName, Set<Long> userIds) {
        // 校验群组是否已满人
        LambdaQueryWrapper<ContactInfoEntity> checkGroupMembersQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
        checkGroupMembersQuery.eq(ContactInfoEntity::getContactId, groupId)
                .eq(ContactInfoEntity::getContactType, ContactTypeEnum.GROUP.getCode())
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode());
        Long groupMembersCount = contactInfoDao.selectCount(checkGroupMembersQuery);
        SysConfigEntity sysConfigInfo = sysConfigService.getSysConfigInfo();
        if (sysConfigInfo.getMaxGroupCount() <= groupMembersCount) {
            throw new CommonException("群已满人");
        }
        if (groupMembersCount + userIds.size() > sysConfigInfo.getMaxGroupCount()) {
            throw new CommonException("邀请的好友数量超过群最大成员数量");
        }
        // 添加联系人 -> mysql
        LocalDateTime curLocalDateTime = LocalDateTime.now();
        String sessionId = Utils.lsMd5(groupId);
        List<ContactInfoEntity> insertContactInfoEntities = userIds.stream().map(userId -> {
            return new ContactInfoEntity()
                    .setUserId(userId)
                    .setContactId(groupId)
                    .setContactType(ContactTypeEnum.GROUP.getCode())
                    .setLastUpdateTime(LocalDateTimeUtil.toEpochMilli(curLocalDateTime))
                    .setStatus(ContactStatusEnum.FRIEND.getCode());
        }).collect(Collectors.toList());
        insertContactInfoEntities.forEach(x -> {
            ContactInfoEntity checkEntity = contactInfoService.getOne(Wrappers.lambdaQuery(ContactInfoEntity.class)
                    .eq(ContactInfoEntity::getUserId, x.getUserId())
                    .eq(ContactInfoEntity::getContactId, x.getContactId()));
            if (Objects.isNull(checkEntity)) {
                contactInfoService.save(x);
            } else {
                contactInfoService.update(x, Wrappers.lambdaUpdate(ContactInfoEntity.class)
                        .eq(ContactInfoEntity::getUserId, x.getUserId())
                        .eq(ContactInfoEntity::getContactId, x.getContactId()));
            }
        });
        // 添加会话 -> mysql（TODO 优化点：第一次发送了消息才会建立会话，如果没人发消息，则不建立）
        LambdaQueryWrapper<UserInfoEntity> getUserInfoQuery = Wrappers.lambdaQuery(UserInfoEntity.class);
        getUserInfoQuery.select(UserInfoEntity::getUserId, UserInfoEntity::getNickname)
                .in(UserInfoEntity::getId, userIds);
        List<UserInfoEntity> userInfoEntities = userInfoDao.selectList(getUserInfoQuery);
        if (CollUtil.isEmpty(userInfoEntities)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        String msg = StrUtil.format(MsgTypeEnum.ADD_GROUP.getInitMsg()
                , userInfoEntities.stream().map(UserInfoEntity::getNickname).collect(Collectors.joining(",")));
        ChatSessionEntity chatSessionEntity = new ChatSessionEntity()
                .setId(sessionId)
                .setLastMsg(msg)
                .setLastReceiveTime(DateUtil.date(curLocalDateTime).getTime());
        if (!chatSessionService.saveOrUpdate(chatSessionEntity)) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        List<ChatUserSessionEntity> batchChatUserSessionList = userIds.stream().map(userId -> {
            return new ChatUserSessionEntity()
                    .setSessionId(sessionId)
                    .setUserId(userId)
                    .setReceiverId(groupId)
                    .setReceiverNickname(groupNickName);
        }).collect(Collectors.toList());
        batchChatUserSessionList.forEach(x -> {
            ChatUserSessionEntity checkChatUserSessionEntity = chatUserSessionService.getOne(Wrappers.lambdaQuery(ChatUserSessionEntity.class)
                    .eq(ChatUserSessionEntity::getSessionId, sessionId)
                    .eq(ChatUserSessionEntity::getUserId, x.getUserId()));
            if (Objects.isNull(checkChatUserSessionEntity)) {
                chatUserSessionService.save(x);
            } else {
                chatUserSessionService.update(x, Wrappers.lambdaUpdate(ChatUserSessionEntity.class)
                        .eq(ChatUserSessionEntity::getSessionId, sessionId)
                        .eq(ChatUserSessionEntity::getUserId, x.getUserId()));
            }
        });
        // 添加消息 -> mongo
        ChatMsgDoc chatMsgDoc = new ChatMsgDoc()
                .setId(Utils.uniqueId())
                .setMsg(msg)
                .setMsgType(MsgTypeEnum.ADD_GROUP.getCode())
                .setReceiverId(groupId)
                .setReceiverType(ContactTypeEnum.GROUP.getCode())
                .setSessionId(sessionId)
                .setSendTime(DateUtil.date(curLocalDateTime).getTime())
                .setStatus(ChatMsgStatusEnum.SUCCESS.getCode());
        mongoTemplate.insert(chatMsgDoc);
        // 处理缓存
        Key key = new ZhiChat(Keys.CONTACT_GROUP);
        List<Channel> userChannelList = userIds.stream().map(userId -> {
            redisRepository.add_2(key, Sets.newHashSet(groupId), userId);
            return channelContextCommon.getUserChannel(userId);
        }).filter(Objects::nonNull).collect(Collectors.toList());
        channelContextCommon.putAllUserChannelToGroupChannel(groupId, userChannelList);
        // 发送消息
        LambdaQueryWrapper<ContactInfoEntity> getGroupMembersQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
        getGroupMembersQuery.eq(ContactInfoEntity::getContactId, groupId)
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode())
                .eq(ContactInfoEntity::getContactType, ContactTypeEnum.GROUP.getCode());
        Long memberCount = contactInfoDao.selectCount(getGroupMembersQuery);
        MsgVO<String> msgVO = new MsgVO<>();
        BeanUtils.copyProperties(chatMsgDoc, msgVO);
        msgVO.setMsgId(chatMsgDoc.getId())
                .setReceiverNickname(groupNickName)
                .setMemberCount(Math.toIntExact(memberCount));
        globalMsgHandle.sendHandler(msgVO);
    }

    @Transactional(rollbackFor = Exception.class)
    public void userBatchLeaveGroup(Long groupId, String groupNickName, Set<Long> userIds, MsgTypeEnum msgTypeEnum) {
        if (MsgTypeEnum.KICK_GROUP != msgTypeEnum && MsgTypeEnum.EXIT_GROUP != msgTypeEnum) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        // 删除联系人 -> mysql
        LambdaUpdateWrapper<ContactInfoEntity> delContactInfoEntityQuery = Wrappers.lambdaUpdate(ContactInfoEntity.class);
        delContactInfoEntityQuery.set(ContactInfoEntity::getStatus, msgTypeEnum.equals(MsgTypeEnum.KICK_GROUP) ? ContactStatusEnum.DELETED_BY_FRIEND.getCode() : ContactStatusEnum.DELETED_FRIEND.getCode())
                .eq(ContactInfoEntity::getContactId, groupId)
                .in(ContactInfoEntity::getUserId, userIds)
                .eq(ContactInfoEntity::getContactType, ContactTypeEnum.GROUP.getCode())
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode());
        if (0 == contactInfoDao.update(null, delContactInfoEntityQuery)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        LambdaQueryWrapper<UserInfoEntity> getUserInfoQuery = Wrappers.lambdaQuery(UserInfoEntity.class);
        getUserInfoQuery.select(UserInfoEntity::getUserId, UserInfoEntity::getNickname)
                .in(UserInfoEntity::getUserId, userIds);
        List<UserInfoEntity> userInfoEntities = userInfoDao.selectList(getUserInfoQuery);
        if (CollUtil.isEmpty(userInfoEntities)) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        String sessionId = Utils.lsMd5(groupId);
        String msg = StrUtil.format(msgTypeEnum.getInitMsg()
                , userInfoEntities.stream().map(UserInfoEntity::getNickname).collect(Collectors.joining(",")));
        LocalDateTime curLocalDateTime = LocalDateTime.now();
        // 修改会话 -> mysql
        ChatSessionEntity updateChatSession = new ChatSessionEntity()
                .setLastReceiveTime(DateUtil.date(curLocalDateTime).getTime())
                .setId(sessionId)
                .setLastMsg(msg);
        if (chatSessionDao.updateById(updateChatSession) != 1) {
            throw CommonException.DB_MODIFY_COMMON_EXCEPTION;
        }
        // 添加消息 -> mongo
        ChatMsgDoc chatMsgDoc = new ChatMsgDoc()
                .setId(Utils.uniqueId())
                .setMsg(msg)
                .setMsgType(msgTypeEnum.getCode())
                .setReceiverId(groupId)
                .setReceiverType(ContactTypeEnum.GROUP.getCode())
                .setSessionId(sessionId)
                .setSendTime(DateUtil.date(curLocalDateTime).getTime())
                .setStatus(ChatMsgStatusEnum.SUCCESS.getCode());
        mongoTemplate.insert(chatMsgDoc);
        // 处理缓存
        List<Channel> userChannelList = userIds.stream().map(userId -> {
            Key key = new ZhiChat(Keys.CONTACT_GROUP);
            redisRepository.sRemove(key, Sets.newHashSet(groupId), userId);
            return channelContextCommon.getUserChannel(userId);
        }).collect(Collectors.toList());
        channelContextCommon.delAllUserChannelToGroupChannel(groupId, userChannelList);
        // 发送消息
        LambdaQueryWrapper<ContactInfoEntity> getGroupMembersQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
        getGroupMembersQuery.eq(ContactInfoEntity::getContactId, groupId)
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode())
                .eq(ContactInfoEntity::getContactType, ContactTypeEnum.GROUP.getCode());
        Long memberCount = contactInfoDao.selectCount(getGroupMembersQuery);
        MsgVO<String> msgVO = new MsgVO<>();
        BeanUtils.copyProperties(chatMsgDoc, msgVO);
        msgVO.setMsgId(chatMsgDoc.getId())
                .setReceiverNickname(groupNickName)
                .setMemberCount(Math.toIntExact(memberCount));
        globalMsgHandle.sendHandler(msgVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean disbandSelfGroup(Long groupId) {
        LambdaQueryWrapper<GroupInfoEntity> queryWrapper = Wrappers.lambdaQuery(GroupInfoEntity.class);
        queryWrapper.eq(GroupInfoEntity::getGroupId, groupId)
                .eq(GroupInfoEntity::getStatus, GroupStatusEnum.GROUP_STATUS_NORMAL.getCode())
                .eq(GroupInfoEntity::getGroupOwnerId, ThreadLocalCache.getUserId());
        if (this.count(queryWrapper) != 1) {
            throw CommonException.PARAMETER_COMMON_EXCEPTION;
        }
        LambdaQueryWrapper<ContactInfoEntity> checkGroupContactQuery = Wrappers.lambdaQuery(ContactInfoEntity.class);
        checkGroupContactQuery.eq(ContactInfoEntity::getContactId, groupId)
                .eq(ContactInfoEntity::getStatus, ContactStatusEnum.FRIEND.getCode())
                .eq(ContactInfoEntity::getContactType, ContactTypeEnum.GROUP.getCode());
        Long contactCount = contactInfoDao.selectCount(checkGroupContactQuery);
        if (contactCount > 1) {
            throw new CommonException("需要先移除其他所有成员");
        }
        return disbandGroup(groupId, ThreadLocalCache.getUserId());
    }
}